/**
 *<p>Copyright: Copyright (c) 2012</p> 
 *<p>Company: SCPII</p>
 *<p>Project:scpii-api</p>
 *@author xiezhouyan
 *@Date 2013-4-11 下午3:24:30
 *@version 1.0
 *@description:
 */
package com.scpii.api.common.aop;

import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.scpii.api.common.annoation.ActionTypeFillIn;
import com.scpii.api.common.annoation.ActionTypeMethod;
import com.scpii.api.common.annoation.ActionTypeTarget;
import com.scpii.api.common.exception.ServerErrorException;
import com.scpii.api.domain.common.Field.FieldStatus;
import com.scpii.api.domain.content.Comments;
import com.scpii.api.domain.user.UserActionPointConfig;
import com.scpii.api.domain.user.UserActions;
import com.scpii.api.domain.user.UserActions.ActionType;
import com.scpii.api.domain.user.UserShare;
import com.scpii.api.service.common.FieldService;
import com.scpii.api.service.user.UserActionPointConfigService;
import com.scpii.api.service.user.UserActionsService;
import com.scpii.api.util.PrincipalUtil;

@Component
@Aspect
public class ActionTypeAspect {

	public static final Map<String, Map<String, Object>> USER_ACTIONT_TYPE_TABLE_FIELD = new HashMap<String, Map<String, Object>>();

	static {

		Map<String, Object> TABLE_FIELD = new HashMap<String, Object>();
		TABLE_FIELD.put("TABLES", new String[] { "ACTIVITIES",
				"UNIVERSAL_CONTENT_TAB" });
		TABLE_FIELD
				.put("FIELDS", new String[] { "JOINED_COUNT", "JOIN_COUNT" });
		TABLE_FIELD.put("IDS", new String[] { "ID", "INT_CONTENT_ID" });
		USER_ACTIONT_TYPE_TABLE_FIELD.put("ACTIVITIES_JOIN", TABLE_FIELD);

		TABLE_FIELD = new HashMap<String, Object>();
		TABLE_FIELD.put("TABLES", new String[] { "ACTIVITIES",
				"UNIVERSAL_CONTENT_TAB" });
		TABLE_FIELD.put("FIELDS", new String[] { "FAV_COUNT", "FAV_COUNT" });
		TABLE_FIELD.put("IDS", new String[] { "ID", "INT_CONTENT_ID" });
		USER_ACTIONT_TYPE_TABLE_FIELD.put("ACTIVITIES_FAV", TABLE_FIELD);

		TABLE_FIELD = new HashMap<String, Object>();
		TABLE_FIELD.put("TABLES", "EC_PRODUCT");
		TABLE_FIELD.put("FIELDS", "FAV_COUNT");
		TABLE_FIELD.put("IDS", "ID");
		USER_ACTIONT_TYPE_TABLE_FIELD.put("EC_FAVORITE", TABLE_FIELD);

		TABLE_FIELD = new HashMap<String, Object>();
		TABLE_FIELD.put("TABLES", new String[] { "ACTIVITIES", "EC_PRODUCT",
				"UNIVERSAL_CONTENT_TAB" });
		TABLE_FIELD.put("FIELDS", new String[] { "COMMENT_COUNT",
				"COMMENT_COUNT", "COMMENT_COUNT" });
		TABLE_FIELD.put("IDS", new String[] { "ID", "ID", "INT_CONTENT_ID" });
		USER_ACTIONT_TYPE_TABLE_FIELD.put("COMMENTS_ADD", TABLE_FIELD);

	}

	@Resource
	private UserActionPointConfigService userActionPointConfigService;

	@Resource
	private UserActionsService userActionsService;

	@Resource
	private FieldService fieldService;

	@Pointcut("execution(* com.scpii.api.*..*.*(..))&&@annotation(com.scpii.api.common.annoation.ActionTypeMethod)")
	public void foundActionTypeMethod() {
	}

	@Pointcut("execution(* com.scpii.api.*..*.*(..))&&@annotation(com.scpii.api.common.annoation.ActionTypeFillIn)")
	public void foundActionTypeFillIn() {
	}

	@Pointcut("execution(* com.scpii.api.*..*.*(..))&&@annotation(com.scpii.api.common.annoation.ActionTypeTarget)")
	public void foundActionTypeTarget() {

	}

	@Pointcut("execution(* com.scpii.api.*..*.*(..))&&@annotation(com.scpii.api.common.annoation.ActionTypeParameter)")
	public void foundActionTypeParameter() {
	}

	@Pointcut("execution(* com.scpii.api.service.user.UserActionsService.insert*(..))")
	public void foundAddUserAction() {
	}

	@Pointcut("execution(* com.scpii.api.service.user.UserActionsService.delete*(..))")
	public void foundDelUserAction() {
	}

	@Pointcut("execution(* com.scpii.api.service.content.CommentsService.insert*(..))")
	public void foundAddComments() {
	}

	@AfterReturning("foundActionTypeMethod()")
	public void foundMethodAfterReturing(JoinPoint joinPoint) {
		ActionTypeMethod actionTypeAnno = getActionTypeAnno(joinPoint);
		String key = actionTypeAnno.value().getValue();
		userActionsService.insertSelective(getUserAction(key),
				getPointConfig(key));
	}

	@AfterReturning("foundActionTypeFillIn()")
	public void foundActionTypeFillInAfterReturing(JoinPoint joinPoint) {
		ActionTypeFillIn actionTypeAnno = getActionTypeFillAnno(joinPoint);
		String key = actionTypeAnno.value().getValue();
		Long userId = PrincipalUtil.getPrincipal().getId();
		Long[] fields = fieldService.selectRequiredIdsNotFillIn(userId,
				FieldStatus.ADD_REQUIRED_SHOW.getValue());
		if (fields == null || fields.length == 0) {
			userActionsService.insertSelective(getUserAction(key),
					getPointConfig(key));
		}

	}

	@AfterReturning("foundActionTypeTarget()&&args(targetId,..)")
	public void foundTargetReturing(JoinPoint joinPoint, Long targetId) {
		ActionTypeTarget actionTypeAnno = getActionTypeTargetAnno(joinPoint);
		if (ActionType.NONE == actionTypeAnno.value()) {
			throw new ServerErrorException("actionType must be provided");
		}
		String key = actionTypeAnno.value().getValue();
		userActionsService.insertSelective(getUserAction(key, targetId),
				getPointConfig(key, targetId));
	}

	@AfterReturning(pointcut = "foundActionTypeParameter()&&args(targetId,actionType,userActions,userShare,..)", returning = "flag")
	public void foundParameterAfterReturing(JoinPoint joinPoint, Long targetId,
			String actionType, UserActions userActions, UserShare userShare,
			Boolean flag) {
		String key = actionType;
		if (Boolean.TRUE == flag) {
			userActionsService.insertSelective(getUserAction(key, targetId),
					getPointConfig(key, targetId), userShare);
		} else {
			String[] actionTypes = new String[] {
					ActionType.ACTIVITIES_JOIN.getValue(),
					ActionType.ACTIVITIES_FAV.getValue(),
					ActionType.EC_FAVORITE.getValue(),
					ActionType.EC_ORDER_ADD.getValue() };
			userActionsService.deleteByWithInTypes(
					getUserAction(key, targetId), actionTypes);
		}
	}

	@AfterReturning("foundAddComments()&&args(record,..)")
	public void foundAddCommentsAfter(Comments record) {
		String key = record.getParentId() == 0 ? ActionType.COMMENTS_ADD
				.getValue() : ActionType.REPLY_ADD.getValue();
		Long targetId = record.getTargetId();

		userActionsService.insertSelective(
				getUserAction(key, targetId, record.getId()),
				getPointConfig(key, targetId));

	}

	@AfterReturning("foundAddUserAction()&&args(record,..)")
	public void foundAddUserActionAfter(UserActions record) {
		userActionsService.updateCountByUserAction(record, 1);
	}

	@AfterReturning("foundDelUserAction()&&args(record,..)")
	public void foundDelUserActionAfter(UserActions record) {
		userActionsService.updateCountByUserAction(record, -1);
	}

	private UserActions getUserAction(String actionType) {
		UserActions record = new UserActions();
		record.setActionType(actionType);
		record.setCreateBy(PrincipalUtil.getPrincipal().getId());
		record.setCreateDt(new Date());
		record.setUserId(PrincipalUtil.getPrincipal().getId());
		return record;
	}

	private UserActions getUserAction(String actionType, Long targetId) {
		UserActions record = new UserActions();
		record.setActionType(actionType);
		record.setCreateBy(PrincipalUtil.getPrincipal().getId());
		record.setCreateDt(new Date());
		record.setUserId(PrincipalUtil.getPrincipal().getId());
		record.setTargetId(targetId);
		return record;

	}

	private UserActions getUserAction(String actionType, Long targetId,
			Long objectId) {
		UserActions record = new UserActions();
		record.setActionType(actionType);
		record.setCreateBy(PrincipalUtil.getPrincipal().getId());
		record.setCreateDt(new Date());
		record.setUserId(PrincipalUtil.getPrincipal().getId());
		record.setTargetId(targetId);
		record.setObjectId(objectId);
		return record;
	}

	private ActionTypeMethod getActionTypeAnno(JoinPoint joinPoint) {
		MethodSignature methodSignature = (MethodSignature) joinPoint
				.getSignature();
		Method method = methodSignature.getMethod();
		return method.getAnnotation(ActionTypeMethod.class);
	}

	private ActionTypeFillIn getActionTypeFillAnno(JoinPoint joinPoint) {
		MethodSignature methodSignature = (MethodSignature) joinPoint
				.getSignature();
		Method method = methodSignature.getMethod();
		return method.getAnnotation(ActionTypeFillIn.class);
	}

	private ActionTypeTarget getActionTypeTargetAnno(JoinPoint joinPoint) {
		MethodSignature methodSignature = (MethodSignature) joinPoint
				.getSignature();
		Method method = methodSignature.getMethod();
		return method.getAnnotation(ActionTypeTarget.class);
	}

	private PointConfig getPointConfig(String actionType, Long targetId) {
		UserActionPointConfig record = new UserActionPointConfig();
		record.setAppId(PrincipalUtil.getPrincipal().getAppId());
		record.setActionType(actionType);
		record.setTargetId(targetId);
		UserActionPointConfig result = userActionPointConfigService
				.selectPointConfigBySelective(record);
		return result == null ? null : new PointConfig(result);
	}

	private PointConfig getPointConfig(String actionType) {
		UserActionPointConfig record = new UserActionPointConfig();
		record.setAppId(PrincipalUtil.getPrincipal().getAppId());
		record.setActionType(actionType);
		UserActionPointConfig result = userActionPointConfigService
				.selectPointConfigBySelective(record);
		return result == null ? null : new PointConfig(result);
	}

	public class PointConfig {

		private final Pattern MAX_IN_PATTERN = Pattern
				.compile("^[0-9]+||([0-9]+/(m|h|d|M|y))$");

		private final String MAX_IN_SEPARATOR = "/";

		private Long id;

		private boolean isMatchMaxIn;

		private String[] maxIn;

		private Integer addPoints;

		private String actionType;

		public PointConfig(UserActionPointConfig config) {
			super();
			this.id = config.getId();
			this.maxIn = config.getMaxIn() == null ? null : config.getMaxIn()
					.split(MAX_IN_SEPARATOR);
			this.isMatchMaxIn = config.getMaxIn() == null ? false
					: MAX_IN_PATTERN.matcher(config.getMaxIn()).matches();
			this.addPoints = config.getAddPoints();
			this.actionType = config.getActionType();
		}

		public boolean isMathMaxIn() {
			return this.isMatchMaxIn;
		}

		public Long getId() {
			return this.id;
		}

		public Integer getMax() {
			return !isMathMaxIn() ? null : Integer.parseInt(maxIn[0]);
		}

		public Integer getAddPoints() {
			return this.addPoints;
		}

		public String getActionType() {
			return this.actionType;
		}

		public Date getTimeAfter() {
			if (!isMathMaxIn()) {
				return null;
			}
			if (maxIn.length < 2) {
				return null;
			}
			String key = maxIn[1];
			Date now = new Date();
			if ("y".equals(key)) {
				return DateUtils.addYears(now, -1);
			} else if ("M".equals(key)) {
				return DateUtils.addMonths(now, -1);
			} else if ("d".equals(key)) {
				return DateUtils.addDays(now, -1);
			} else if ("h".equals(key)) {
				return DateUtils.addHours(now, -1);
			} else if ("m".equals(key)) {
				return DateUtils.addMinutes(now, -1);
			} else {
				return null;
			}
		}

		@Override
		public String toString() {
			return "id: " + id + " maxIn: " + StringUtils.join(maxIn, "/")
					+ " actionType: " + actionType + " timeAfter:"
					+ getTimeAfter();
		}

	}
}
